/*-------------------------------------------------------------------------
 *
 * define.c
 *	  各种对象创建的支持例程。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/define.c
 *
 * DESCRIPTION
 *	  "DefineFoo" 例程获取解析树并挑选出合适的参数/标志，将结果传递给相应的 "FooDefine" 例程（在 src/catalog 中）以执行实际的目录修改操作。  这些例程还验证用户执行命令的权限。
 *
 * NOTES
 *	  这些事情必须按以下顺序定义和提交：
 *		"create function":
 *				input/output, recv/send 过程
 *		"create type":
 *				type
 *		"create operator":
 *				operators
 *
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>
#include <math.h>

#include "catalog/namespace.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "parser/scansup.h"
#include "utils/builtins.h"

/*
 * 从DefElem中提取字符串值（否则不作解释）。
 */
char * defGetString(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a parameter",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return psprintf("%ld", (long) intVal(fc_def->arg));
		case T_Float:
			return castNode(Float, fc_def->arg)->fval;
		case T_Boolean:
			return boolVal(fc_def->arg) ? "true" : "false";
		case T_String:
			return strVal(fc_def->arg);
		case T_TypeName:
			return TypeNameToString((TypeName *) fc_def->arg);
		case T_List:
			return NameListToString((List *) fc_def->arg);
		case T_A_Star:
			return pstrdup("*");
		default:
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_def->arg));
	}
	return NULL;				/* 保持编译器安静 */
}

/*
 * 从DefElem中提取数字值（实际上是双精度浮点数）。
 */
double defGetNumeric(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a numeric value",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return (double) intVal(fc_def->arg);
		case T_Float:
			return floatVal(fc_def->arg);
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("%s requires a numeric value",
							fc_def->defname)));
	}
	return 0;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取布尔值。
 */
bool defGetBoolean(DefElem *fc_def)
{
	/*
	 * 如果未给出参数，则假定为“true”。
	 */
	if (fc_def->arg == NULL)
		return true;

	/*
	 * 允许 0, 1, "true", "false", "on", "off"
	 */
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			switch (intVal(fc_def->arg))
			{
				case 0:
					return false;
				case 1:
					return true;
				default:
					/* 否则，在下面报错 */
					break;
			}
			break;
		default:
			{
				char	   *fc_sval = defGetString(fc_def);

				/*
				 * 此处接受的字符串集合应该与
				 * 语法的opt_boolean_or_string产生匹配。
				 */
				if (pg_strcasecmp(fc_sval, "true") == 0)
					return true;
				if (pg_strcasecmp(fc_sval, "false") == 0)
					return false;
				if (pg_strcasecmp(fc_sval, "on") == 0)
					return true;
				if (pg_strcasecmp(fc_sval, "off") == 0)
					return false;
			}
			break;
	}
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("%s requires a Boolean value",
					fc_def->defname)));
	return false;				/* 保持编译器安静 */
}

/*
 * 从DefElem中提取int32值。
 */
int32 defGetInt32(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires an integer value",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return (int32) intVal(fc_def->arg);
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("%s requires an integer value",
							fc_def->defname)));
	}
	return 0;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取int64值。
 */
int64 defGetInt64(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a numeric value",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return (int64) intVal(fc_def->arg);
		case T_Float:

			/*
			 * 对于int4而言，值过大将通过词法分析器表示为浮点常量。
			 * 如果它们是有效的int8字符串，则接受这些值。
			 */
			return DatumGetInt64(DirectFunctionCall1(int8in,
													 CStringGetDatum(castNode(Float, fc_def->arg)->fval)));
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("%s requires a numeric value",
							fc_def->defname)));
	}
	return 0;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取OID值。
 */
Oid defGetObjectId(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a numeric value",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return (Oid) intVal(fc_def->arg);
		case T_Float:

			/*
			 * 对于int4而言，值过大将通过词法分析器表示为浮点常量。
			 * 如果它们是有效的OID字符串，则接受这些值。
			 */
			return DatumGetObjectId(DirectFunctionCall1(oidin,
														CStringGetDatum(castNode(Float, fc_def->arg)->fval)));
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("%s requires a numeric value",
							fc_def->defname)));
	}
	return 0;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取可能带有前缀的名称（作为字符串列表）。
 */
List * defGetQualifiedName(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a parameter",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_TypeName:
			return ((TypeName *) fc_def->arg)->names;
		case T_List:
			return (List *) fc_def->arg;
		case T_String:
			/* 允许引号名称以兼容旧版本 */
			return list_make1(fc_def->arg);
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("argument of %s must be a name",
							fc_def->defname)));
	}
	return NIL;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取TypeName。
 *
 * 注意：这里我们不接受列表参数，因为当名称看起来像一个运算符名称时，
 * 解析器只会返回一个简单的列表。
 */
TypeName * defGetTypeName(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a parameter",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_TypeName:
			return (TypeName *) fc_def->arg;
		case T_String:
			/* 允许引用类型名称以兼容旧版本 */
			return makeTypeNameFromNameList(list_make1(fc_def->arg));
		default:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("argument of %s must be a type name",
							fc_def->defname)));
	}
	return NULL;				/* 保持编译器安静 */
}

/*
 * 从DefElem中提取类型长度指示符（可以是绝对字节数，或
 * -1 表示“可变”）；
 */
int defGetTypeLength(DefElem *fc_def)
{
	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a parameter",
						fc_def->defname)));
	switch (nodeTag(fc_def->arg))
	{
		case T_Integer:
			return intVal(fc_def->arg);
		case T_Float:
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("%s requires an integer value",
							fc_def->defname)));
			break;
		case T_String:
			if (pg_strcasecmp(strVal(fc_def->arg), "variable") == 0)
				return -1;		/* 可变长度 */
			break;
		case T_TypeName:
			/* 在语法选择相信“可变”是类型名时需适应 */
			if (pg_strcasecmp(TypeNameToString((TypeName *) fc_def->arg),
							  "variable") == 0)
				return -1;		/* 可变长度 */
			break;
		case T_List:
			/* 必须是运算符名称 */
			break;
		default:
			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_def->arg));
	}
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("invalid argument for %s: \"%s\"",
					fc_def->defname, defGetString(fc_def))));
	return 0;					/* 保持编译器安静 */
}

/*
 * 从DefElem中提取字符串值列表（否则不作解释）。
 */
List * defGetStringList(DefElem *fc_def)
{
	ListCell   *fc_cell;

	if (fc_def->arg == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("%s requires a parameter",
						fc_def->defname)));
	if (nodeTag(fc_def->arg) != T_List)
		elog(ERROR, "unrecognized node type: %d", (int) nodeTag(fc_def->arg));

	foreach(fc_cell, (List *) fc_def->arg)
	{
		Node	   *fc_str = (Node *) lfirst(fc_cell);

		if (!IsA(fc_str, String))
			elog(ERROR, "unexpected node type in name list: %d",
				 (int) nodeTag(fc_str));
	}

	return (List *) fc_def->arg;
}

/*
 * 提出关于冲突的DefElem的错误。
 */
void errorConflictingDefElem(DefElem *fc_defel, ParseState *fc_pstate)
{
	ereport(ERROR,
			errcode(ERRCODE_SYNTAX_ERROR),
			errmsg("conflicting or redundant options"),
			parser_errposition(fc_pstate, fc_defel->location));
}
